//
// This software is now distributed according to
// the Lesser Gnu Public License. Please see
// http://www.gnu.org/copyleft/lesser.txt for
// the details.
// -- Happy Computing!
//
package com.stevesoft.pat;

/**
 * This class provides a method for parsing the "s/.../.../" constructs of
 * Regex.perlCode.
 * 
 * @see Regex#perlCode
 */
class parsePerl {

	final static char close(char c) {
		// This switch statement does not behave
		// properly when compiled with jdk1.1.5
		// and the -O flag.
		/*
		 * switch(c) { case '[': return ']'; case '(': return ')'; case '{':
		 * return '}'; } return c;
		 */
		if (c == '<')
			return '>';
		if (c == '[')
			return ']';
		if (c == '(')
			return ')';
		if (c == '{')
			return '}';
		return c;
	}

	final public static String codify(String s, boolean keepbs) {
		return codify(s, 0, s.length(), keepbs);
	}

	final public static String codify(String s, int i0, int iN, boolean keepbs) {
		StringBuffer sb = new StringBuffer();
		boolean ucmode = false, lcmode = false, litmode = false;
		boolean uc1 = false, lc1 = false;
		boolean modified = false;
		for (int i = i0; i < iN; i++) {
			char c = s.charAt(i);
			boolean mf = true, app = true;
			if (c == '\\') {
				app = false;
				i++;
				if (i < s.length()) {
					char c2 = s.charAt(i);
					switch (c2) {
						case 'Q':
							litmode = true;
							break;
						case 'U':
							ucmode = true;
							break;
						case 'L':
							lcmode = true;
							break;
						case 'u':
							uc1 = true;
							break;
						case 'l':
							lc1 = true;
							break;
						case 'E':
							uc1 = lc1 = ucmode = lcmode = litmode = false;
							break;
						default:
							if (keepbs)
								sb.append('\\');
							c = c2;
							if (keepbs)
								mf = false;
							app = true;
							break;
					}
					modified |= mf;
				}
			}
			if (app) {
				if (lc1) {
					c = lc(c);
					lc1 = false;
				}
				else if (uc1) {
					c = uc(c);
					uc1 = false;
				}
				else if (ucmode) {
					c = uc(c);
				}
				else if (lcmode) {
					c = lc(c);
				}
				if (litmode && needbs(c))
					sb.append('\\');
				sb.append(c);
			}
		}
		return modified ? sb.toString() : s;
	}

	final static char uc(char c) {
		return CaseMgr.toUpperCase(c);
	}

	final static char lc(char c) {
		return CaseMgr.toLowerCase(c);
	}

	final static boolean needbs(char c) {
		if (c >= 'a' && c <= 'z')
			return false;
		if (c >= 'A' && c <= 'Z')
			return false;
		if (c >= '0' && c <= '9')
			return false;
		if (c == '_')
			return false;
		return true;
	}

	final static Regex parse(String s) {
		boolean igncase = false, optim = false, gFlag = false;
		boolean sFlag = false, mFlag = false, xFlag = false;
		StringBuffer s1 = new StringBuffer();
		StringBuffer s2 = new StringBuffer();
		int i = 0, count = 0;
		char mode, delim = '/', cdelim = '/';
		if (s.length() >= 3 && s.charAt(0) == 's') {
			mode = 's';
			delim = s.charAt(1);
			cdelim = close(delim);
			i = 2;
		}
		else if (s.length() >= 2 && s.charAt(0) == 'm') {
			mode = 'm';
			delim = s.charAt(1);
			cdelim = close(delim);
			i = 2;
		}
		else if (s.length() >= 1 && s.charAt(0) == '/') {
			mode = 'm';
			i = 1;
		}
		else {
			try {
				RegSyntaxError.endItAll("Regex.perlCode should be of the " + "form s/// or m// or //");
			} catch (RegSyntax rs) {
			}
			return null;
		}
		for (; i < s.length(); i++) {
			if (s.charAt(i) == '\\') {
				s1.append('\\');
				i++;
			}
			else if (s.charAt(i) == cdelim && count == 0) {
				i++;
				break;
			}
			else if (s.charAt(i) == delim && cdelim != delim) {
				count++;
			}
			else if (s.charAt(i) == cdelim && cdelim != delim) {
				count--;
			}
			s1.append(s.charAt(i));
		}
		if (mode == 's' && cdelim != delim) {
			while (i < s.length() && Prop.isWhite(s.charAt(i)))
				i++;
			if (i >= s.length()) {
				try {
					RegSyntaxError.endItAll("" + mode + delim + " needs " + cdelim);
				} catch (RegSyntax rs) {
				}
				return null;
			}
			cdelim = close(delim = s.charAt(i));
			i++;
		}
		count = 0;
		if (mode == 's') {
			for (; i < s.length(); i++) {
				if (s.charAt(i) == '\\') {
					s2.append('\\');
					i++;
				}
				else if (s.charAt(i) == cdelim && count == 0) {
					i++;
					break;
				}
				else if (s.charAt(i) == delim && cdelim != delim) {
					count++;
				}
				else if (s.charAt(i) == cdelim && cdelim != delim) {
					count--;
				}
				s2.append(s.charAt(i));
			}
		}
		for (; i < s.length(); i++) {
			char c = s.charAt(i);
			switch (c) {
				case 'x':
					xFlag = true;
					break;
				case 'i':
					igncase = true;
					break;
				case 'o':
					optim = true;
					break;
				case 's':
					sFlag = true;
					break;
				case 'm':
					mFlag = true;
					break;
				case 'g':
					gFlag = true;
					break;
				default:
					// syntax error!
					try {
						RegSyntaxError.endItAll("Illegal flag to pattern: " + c);
					} catch (RegSyntax rs) {
					}
					return null;
			}
		}
		Regex r = new Regex();
		try {
			String pat = s1.toString(), reprul = s2.toString();
			if (xFlag) {
				pat = strip(pat);
				reprul = strip(reprul);
			}
			r.compile(pat);
			r.ignoreCase |= igncase;
			r.gFlag |= gFlag;
			r.sFlag |= sFlag;
			r.mFlag |= mFlag;
			if (optim)
				r.optimize();
			if (delim == '\'')
				r.setReplaceRule(new StringRule(reprul));
			else
				r.setReplaceRule(ReplaceRule.perlCode(reprul));
		} catch (RegSyntax rs) {
			r = null;
		}
		return r;
	}

	static String strip(String s) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (Prop.isWhite(c)) {
				;
			}
			else if (c == '#') {
				i++;
				while (i < s.length()) {
					if (s.charAt(i) == '\n')
						break;
					i++;
				}
			}
			else if (c == '\\') {
				sb.append(c);
				sb.append(s.charAt(++i));
			}
			else
				sb.append(c);
		}
		return sb.toString();
	}
}
